package server

import (
	"context"
	"github.com/cihub/seelog"
	"github.com/gin-gonic/gin"
	"kyland.com/eagle/api"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"syscall"
	"time"
)

type RouterFunc func(string, ...gin.HandlerFunc) gin.IRoutes
type HandleFunc func(*api.Context)

type Router struct {
	Uri    string
	Method RouterFunc
	Action HandleFunc
}

var (
	router *gin.Engine
)

func initHttp() {
	gin.SetMode(gin.ReleaseMode)
	router = gin.Default()
	router.Use(func(c *gin.Context) {
		start := time.Now()
		seelog.Tracef("%#v", c.Request)
		c.Next()
		latency := time.Now().Sub(start)
		seelog.Infof("%15s %6s %3d %13v %s", c.ClientIP(), c.Request.Method, c.Writer.Status(), latency, c.Request.URL.Path)
	}, func(c *gin.Context) {
		if c.Request.Method == http.MethodOptions {
			ctx := api.CreateContext(c)
			ctx.DontUseJsonSuccess()
		}
	})

	authReg := []string{}

	routerConfig := []Router{
		Router{"/v1/task/:name", router.DELETE, api.DeleteTask},
		Router{"/v1/tasks", router.GET, api.GetTasks},
		Router{"/v1/task/:name", router.GET, api.GetTask},
		Router{"/v1/task/time/:src", router.GET, api.GetTaskTime},
		Router{"/v1/file/sendToPeers", router.POST, api.FileToPeers},
		Router{"/v1/file/share", router.POST, api.ShareAdd},
		Router{"/v1/file/download", router.POST, api.ShareGet},
		Router{"/v1/file/getcid", router.GET, api.GetCidFromFileName},
		Router{"/v1/task/execute", router.POST, api.ExecuteTask},
		Router{"/v1/task/request", router.POST, api.RequestTask},
		Router{"/v1/task/status", router.POST, api.TaskStatus},
		Router{"/v1/task/notify", router.POST, api.TaskNotify},
		Router{"/v1/upload", router.POST, api.Upload},
		Router{"/v1/download/:name", router.GET, api.Download},
	}

	auth := []*regexp.Regexp{}
	for _, reg := range authReg {
		auth = append(auth, regexp.MustCompile(reg))
	}

	needCheckAuth := func(uri string) bool {
		for _, reg := range auth {
			if reg.MatchString(uri) {
				return true
			}
		}
		return false
	}

	for _, config := range routerConfig {
		config.Method(config.Uri, makeHandler(config.Action, needCheckAuth(config.Uri)))
	}
}

func makeHandler(routerFunc HandleFunc, needCheckAuth bool) gin.HandlerFunc {
	return func(gctx *gin.Context) {
		ctx := api.CreateContext(gctx)
		defer func() {
			if r := recover(); r != nil {
				// panic(r)
				ctx.ShowPanicMsg(r)
			}
		}()

		routerFunc(ctx)
	}
}

func StartHttp(addr string) {
	initHttp()
	srv := &http.Server{
		Addr:    addr,
		Handler: router,
	}

	go func() {
		// service connections
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			seelog.Critical("listen: %s\n", err)
		}
	}()

	// Wait for interrupt signal to gracefully shutdown the server with
	// a timeout of 5 seconds.
	quit := make(chan os.Signal)
	// kill (no param) default send syscanll.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	seelog.Infof("Shutdown Server ...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		seelog.Critical("Server Shutdown:", err)
	}
	// catching ctx.Done(). timeout of 5 seconds.
	select {
	case <-ctx.Done():
		seelog.Infof("timeout of 5 seconds.")
	}
	seelog.Infof("Server exiting")
}
